package org.aicfve.previze.solar.utils;

import org.aicfve.previze.solar.entity.Coordinate;
import org.aicfve.previze.solar.entity.SolarAngle;

import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SolarAngleUtils {

    private static Logger logger = Logger.getLogger(SolarAngleUtils.class.toString());

    public static SolarAngle get(Date date, Coordinate coordinate, int TimeZone) {
        logger.setLevel(Level.OFF);
        logger.info("[date]:" + date);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int day = calendar.get(Calendar.DATE);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int min = calendar.get(Calendar.MINUTE);
        int sec = calendar.get(Calendar.SECOND);

        int timezone = date.getTimezoneOffset();

        logger.info("[date.year]:" + year);
        logger.info("[date.month]:" + month);
        logger.info("[date.day]:" + day);
        logger.info("[date.hour]:" + hour);
        logger.info("[date.min]:" + min);
        logger.info("[date.sec]:" + sec);

//        logger.warning(ReflectionToStringBuilder.toString(coordinate));

        double latitude  = coordinate.getLatitude();
        double longitude = coordinate.getLongitude();

        logger.info("[latitude]:" + latitude);
        logger.info("[longitude]:" + longitude);

        logger.info("[date.timezone]:" + timezone);

        // fix:修正了平年和闰年
        double N = getN(date);

        double N0 = 79.6764 + 0.2422 * (year - 1985) - Math.floor((year - 1985) / 4.0);

        double t = N - N0;

        // todo:365.2422是地球日的平均时间，但是实际分配到每一天时，会发生偏移。则需要根据不同天进行重新计算。

        // todo:需要根据天文日历来确定数据偏移

        // 日角
        double sitar = 2 * Math.PI * t / 365.2422;

        logger.info("[日角sitar]:" + sitar);

        // 太阳赤纬角
        double ED = 0.3723 + 23.2567 * Math.sin(sitar) + 0.1149 * Math.sin(2 * sitar) - 0.1712 * Math.sin(3 * sitar) - 0.758 * Math.cos(sitar) + 0.3656 * Math.cos(2 * sitar) + 0.0201 * Math.cos(3 * sitar);

        ED = ED * Math.PI / 180;
        logger.info("[太阳赤纬角ED]:" + ED);




        double dLon = 0.0;
        //地球上某一点与其所在时区中心的经度差
        if (longitude >= 0) {
            if (TimeZone == -1) {
                dLon = longitude - (Math.floor((longitude * 10 - 75) / 150) + 1) * 15.0;
            } else {
                dLon = longitude - TimeZone * 15.0;
            }
        } else {
            if (TimeZone == -1) {
                dLon = (Math.floor((longitude * 10 - 75) / 150) + 1) * 15.0 - longitude;
            } else {
                dLon = TimeZone * 15.0 - longitude;
            }
        }

        // 时差
        double Et = 0.0028 - 1.9857 * Math.sin(sitar) + 9.9059 * Math.sin(2 * sitar) - 7.0924 * Math.cos(sitar) - 0.6882 * Math.cos(2 * sitar);
        logger.info("[时差Et]:" + Et);

        // 地方时
        double gtdt = hour + min / 60.0 + sec / 3600.0 + dLon / 15;
        gtdt = gtdt + Et / 60.0;
        double dTimeAngle = 15.0 * (gtdt - 12);
        dTimeAngle = dTimeAngle * Math.PI / 180;
        double latitudeArc = latitude * Math.PI / 180;

        double HeightAngleArc = Math.asin(Math.sin(latitudeArc) * Math.sin(ED) + Math.cos(latitudeArc) * Math.cos(ED) * Math.cos(dTimeAngle));

        double CosAzimuthAngle = (Math.sin(HeightAngleArc) * Math.sin(latitudeArc) - Math.sin(ED)) / Math.cos(HeightAngleArc) / Math.cos(latitudeArc);
        double AzimuthAngleArc = Math.acos(CosAzimuthAngle);

        // 计算最终结果
         double ElevationAngle = HeightAngleArc * 180 / Math.PI;

        double ZenithAngle = 90 - ElevationAngle;


        double AzimuthAngle = AzimuthAngleArc * 180 / Math.PI;
        if (dTimeAngle < 0) {
            AzimuthAngle = 180 - AzimuthAngle;
        } else {
            AzimuthAngle = 180 + AzimuthAngle;
        }

        SolarAngle solarAngle = new SolarAngle();
        solarAngle.setAzimuthAngle(AzimuthAngle);
        solarAngle.setElevationAngle(ElevationAngle);
        solarAngle.setZenithAngle(ZenithAngle);

        return solarAngle;
    }

    private static int getN(Date date) {
        int year = date.getYear();
        int month = date.getMonth();
        int day = date.getDate();

        int DayOfYear;
        int FebDays;

        if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
            FebDays = 29;
        else
            FebDays = 28;

        DayOfYear = day;
        if (month > 1) DayOfYear = DayOfYear + 31;
        if (month > 2) DayOfYear = DayOfYear + FebDays;
        if (month > 3) DayOfYear = DayOfYear + 31;
        if (month > 4) DayOfYear = DayOfYear + 30;
        if (month > 5) DayOfYear = DayOfYear + 31;
        if (month > 6) DayOfYear = DayOfYear + 30;
        if (month > 7) DayOfYear = DayOfYear + 31;
        if (month > 8) DayOfYear = DayOfYear + 31;
        if (month > 9) DayOfYear = DayOfYear + 30;
        if (month > 10) DayOfYear = DayOfYear + 31;
        if (month > 11) DayOfYear = DayOfYear + 30;
        return DayOfYear;
    }
}
